﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace csctp
{
    public class CTPQuote
    {
        ctp_quote _q;
        private int nRequestID = 0;
        private readonly List<Delegate> _listDele = new List<Delegate>();
        private ConcurrentDictionary<string, CThostFtdcDepthMarketDataField> _ticks = new ConcurrentDictionary<string, CThostFtdcDepthMarketDataField>();

        /// <summary>
        /// 
        /// </summary>
        public CTPQuote()
        {
            _q = new ctp_quote();
            SetCallBack();
        }

        /// <summary>
        /// 帐号
        /// </summary>
        public string Investor { get; private set; }

        /// <summary>
        /// 密码
        /// </summary>
        public string Password { get; private set; }

        /// <summary>
        /// 经纪商代码
        /// </summary>

        public string Broker { get; private set; }
        public bool IsLogin { get; protected set; }

        /// <summary>
        /// Tick数据
        /// </summary>
        public ConcurrentDictionary<string, CThostFtdcDepthMarketDataField> DicTick { get { return _ticks; } }

        Delegate AddDele(Delegate d) { _listDele.Add(d); return d; }

        void SetCallBack()
        {
            _q.OnFrontConnected = CTPOnFrontConnected;
            _q.OnRspUserLogin = CTPOnRspUserLogin;
            _q.OnFrontDisconnected = CTPOnFrontDisconnected;
            _q.OnRtnDepthMarketData = CTPOnRtnDepthMarketData;
        }
        private void CTPOnFrontDisconnected(int nReason)
        {
            this.IsLogin = false;
            _OnFrontDisConnected?.Invoke(this, new IntEventArgs { Value = nReason });
            SetCallBack();
        }

        private void CTPOnFrontConnected()
        {
            _OnFrontConnected?.Invoke(this, new EventArgs());
        }

        private void CTPOnRspUserLogin(CThostFtdcRspUserLoginField pRspUserLogin, CThostFtdcRspInfoField pRspInfo, int nRequestID, bool bIsLast)
        {
            if (pRspInfo.ErrorID == 0)
            {
                this.IsLogin = true;
            }
            _OnRspUserLogin?.Invoke(this, new ErrorEventArgs { ErrorID = pRspInfo.ErrorID, ErrorMsg = pRspInfo.ErrorMsg, });
        }

        private void CTPOnRtnDepthMarketData(CThostFtdcDepthMarketDataField pDepthMarketData)
        {
            if (string.IsNullOrEmpty(pDepthMarketData.InstrumentID) || string.IsNullOrEmpty(pDepthMarketData.UpdateTime) || double.IsInfinity(pDepthMarketData.UpperLimitPrice))//过滤无穷大/小
            {
                return;
            }
            //修正last=double.max
            if (Math.Abs(pDepthMarketData.LastPrice - double.MaxValue) < double.Epsilon)
            {
                if (Math.Abs(pDepthMarketData.AskPrice1 - double.MaxValue) > double.Epsilon)
                {
                    pDepthMarketData.LastPrice = pDepthMarketData.AskPrice1;
                }
                else if (Math.Abs(pDepthMarketData.BidPrice1 - double.MaxValue) > double.Epsilon)
                {
                    pDepthMarketData.LastPrice = pDepthMarketData.BidPrice1;
                }
                else
                    return;
            }

            //去掉tradingday字段
            //if (string.IsNullOrEmpty(f.TradingDay))
            //{
            //	f.TradingDay = this.TradingDay; //日期:实盘中某些交易所,此字段为空
            //}
            //if (string.IsNullOrEmpty(f.ActionDay)) //此字段可能为空
            //{
            //	f.ActionDay = this.TradingDay;
            //}
            //f.ExchangeID = instrument.ExchangeID;
            //处理,单边有挂边的情况
            if (pDepthMarketData.AskPrice1 > pDepthMarketData.UpperLimitPrice) //未赋值的数据
            {
                pDepthMarketData.AskPrice1 = pDepthMarketData.LastPrice;
            }
            if (pDepthMarketData.BidPrice1 > pDepthMarketData.UpperLimitPrice)
            {
                pDepthMarketData.BidPrice1 = pDepthMarketData.LastPrice;
            }
            //修最高/最低
            if (Math.Abs(pDepthMarketData.HighestPrice - double.MaxValue) < double.Epsilon)
            {
                pDepthMarketData.HighestPrice = pDepthMarketData.AskPrice1;
            }
            if (Math.Abs(pDepthMarketData.LowestPrice - double.MaxValue) < double.Epsilon)
            {
                pDepthMarketData.LowestPrice = pDepthMarketData.BidPrice1;
            }

            if (!DicTick.TryAdd(pDepthMarketData.InstrumentID, pDepthMarketData))
            {
                DicTick[pDepthMarketData.InstrumentID] = pDepthMarketData;
            }

            if (_OnRtnTick == null) return;
            _OnRtnTick(this, new TickEventArgs
            {
                Tick = pDepthMarketData,
            });
        }

        public delegate void RtnTick(object sender, TickEventArgs e);
        internal RtnTick _OnRtnTick;
        public event RtnTick OnRtnTick
        {
            add { _OnRtnTick += value; }
            remove { _OnRtnTick -= value; }
        }

        public delegate void FrontDisconnected(object sender, IntEventArgs e);
        FrontDisconnected _OnFrontDisConnected;
        public event FrontDisconnected OnFrontDisconnected
        {
            add { _OnFrontDisConnected += value; }
            remove { _OnFrontDisConnected -= value; }
        }

        public delegate void RspUserLogin(object sender, ErrorEventArgs e);
        internal RspUserLogin _OnRspUserLogin;
        public event RspUserLogin OnRspUserLogin
        {
            add { _OnRspUserLogin += value; }
            remove { _OnRspUserLogin -= value; }
        }

        public delegate void FrontConnected(object sender, EventArgs e);
        internal FrontConnected _OnFrontConnected;
        public event FrontConnected OnFrontConnected
        {
            add { _OnFrontConnected += value; }
            remove { _OnFrontConnected -= value; }
        }


        public void ReqConnect(string addr)
        {
            _q.RegisterFront(addr);
            //_q.Init();
            _q.Init();
            //_q.Join(); //会造成阻塞
        }

        public void ReqSubscribeMarketData(params string[] pInstrument)
        {
            int size = Marshal.SizeOf(typeof(IntPtr));
            IntPtr insts = Marshal.AllocHGlobal(size * pInstrument.Length);
            var tmp = insts;
            for (int i = 0; i < pInstrument.Length; i++, tmp += size)
            {
                Marshal.StructureToPtr(Marshal.StringToHGlobalAnsi(pInstrument[i]), tmp, false);
            }
            _q.SubscribeMarketData(insts, pInstrument.Length);
        }

        public void ReqUnSubscribeMarketData(params string[] pInstrument)
        {
            int size = Marshal.SizeOf(typeof(IntPtr));
            IntPtr insts = Marshal.AllocHGlobal(size * pInstrument.Length);
            var tmp = insts;
            for (int i = 0; i < pInstrument.Length; i++, tmp += size)
            {
                Marshal.StructureToPtr(Marshal.StringToHGlobalAnsi(pInstrument[i]), tmp, false);
            }
            _q.UnSubscribeMarketData(insts, pInstrument.Length);
        }

        public void ReqUserLogin(string broker, string investor, string password)
        {
            this.Broker = broker;
            this.Investor = investor;
            this.Password = password;
            var f = new CThostFtdcReqUserLoginField
            {
                BrokerID = this.Broker,
                UserID = this.Investor,
                Password = this.Password,
            };
            _q.ReqUserLogin(f, this.nRequestID++);
        }

        public void ReqRelease()
        {
            this.IsLogin = false;
            _q.Release();
            //上面的disconnect注销掉,需要主动调用此回调函数
            _OnFrontDisConnected?.Invoke(this, new IntEventArgs { Value = 0 });
            //取消连接响应,避免重连后的再登录.（release中已处理）
            //_q.SetOnFrontDisconnected(null);
            //_q.SetOnFrontConnected(null);
        }
    }
}